package com.songyang.blog.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.songyang.blog.entity.Post;
import com.songyang.blog.mapper.PostMapper;
import com.songyang.blog.service.PostService;
import com.songyang.blog.util.RedisUtil;
import com.songyang.blog.vo.PostVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 宋阳
 * @since 2021-05-07
 */
@Service
class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService {
    @Autowired
    PostMapper postMapper;
    @Autowired
    RedisUtil redisUtil;

    @Override
    public IPage<PostVO> paging(Page page, Long categoryId, Long userId, Integer level, String order) {
        if (level == null)
            level = -1;
        QueryWrapper<Post> wrapper = new QueryWrapper<Post>()
                .eq(categoryId != null, "categoryId", categoryId)
                .eq(userId != null, "userId", userId)
                .eq(level == 0, "level", 0)
                .gt(level >= 0, "level", 0)
                .orderByDesc(order != null, order);
        return postMapper.selectPosts(page, wrapper);
    }

    @Override
    public PostVO selectOnePost(QueryWrapper<Post> queryWrapper) {
        return postMapper.selectOnePost(queryWrapper);
    }

    /**
     * 本周热议初始化，获得文章的基本信息和热榜的信息
     */
    @Override
    public void initWeekRank() {
        //获取七天内的评论的文章
        List<Post> postList = this.list(new QueryWrapper<Post>()
                .ge("created", DateUtil.offsetDay(new Date(), -7))//规定查询的时间为当前时间的前七天
                .select("id,title,user_id,comment_count,view_count,created")//在数据库当中查询需要的字段
        );


//        IPage<Post> page = new Page<>(0, 6);
//        List<Post> postList = this.page(page, new QueryWrapper<Post>()
//                .select("id,title,user_id,comment_count,view_count,created")//在数据库当中查询需要的字段
//                .orderByDesc("created")).getRecords();

        //文章的总评论量
        for (Post post : postList) {

            //给数据库当中的创建时间转换成需要的格式
            String key = "day:rank:" + DateUtil.format(post.getCreated(), DatePattern.PURE_DATE_FORMAT);
            redisUtil.zSet(key, post.getId(), post.getCommentCount());//key：post的ID，评论数
            //设置七天后自动过期
            redisUtil.expire(key, getExpireTime(post));//设置好过期时间
            //缓存文章的一些基本信息（评论标题 等等..）
            this.hashCachePostIdAndTile(post, getExpireTime(post));
        }
        //做并集获得一周的阅读量
        this.zUnionAndStoreLast7DayForWeekRank();

    }

    /*
    有删除或者增减评论的时候调用这个方法，对Redis当中的数据进行操作，并且对热榜进行一次更新
     */
    @Override
    public void incrCommentAndUnionForWeekRank(Long postId, boolean isIncr) {
        String currentKey = "day:rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
        redisUtil.zIncrementScore(currentKey, postId, isIncr ? 1 : -1);//如果是增加就加一，不是的话就减一。
        Post post = this.getById(postId);//根据这个id，在数据库当中查询到这个post实体类
        hashCachePostIdAndTile(post, getExpireTime(post));
        zUnionAndStoreLast7DayForWeekRank();//评论改变以后，重新做一次并集。保证热榜实时更新
    }

    @Override
    public void putVIewCount(PostVO vo) {
//        String viewKey=get+"post:"vo.getId();
        String key = "rank:post:" + vo.getId();
        //从缓存当中获取这个浏览量
        Integer viewCount = (Integer) redisUtil.hget(key, "post:viewCount");
        //如果缓存没有，就先从数据库里面获取然后自加一次
        if (viewCount != null) {
            vo.setViewCount(viewCount + 1);
        } else {
            vo.setViewCount(vo.getViewCount() + 1);
        }
        //同步到缓存
        redisUtil.hset(key, "post:viewCount", vo.getViewCount());
    }

    /*
    本周并集合并评论数量操作
     */
    private void zUnionAndStoreLast7DayForWeekRank() {
        String currentKey = "day:rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
        String destkey = "week:rank";//目标的key
        List<String> othersKeys = new ArrayList<>();
        for (int i = -6; i <= 0; i++) {
            String temp = "day:rank:" +
                    DateUtil.format(DateUtil.offsetDay(new Date(), i), DatePattern.PURE_DATE_FORMAT);
            othersKeys.add(temp);
        }
        redisUtil.zUnionAndStore(currentKey, othersKeys, destkey);
    }

    /*
    缓存文章的信息
     */
    private void hashCachePostIdAndTile(Post post, long expireTime) {
        String key = "rank:post:" + post.getId();
        boolean hasKey = redisUtil.hasKey(key);
        if (!hasKey) {
            redisUtil.hset(key, "post:id", post.getId(), expireTime);//文章的id
            redisUtil.hset(key, "post:title", post.getTitle(), expireTime);//文章的标题
            redisUtil.hset(key, "post:commentCount", post.getCommentCount(), expireTime);//评论数
            redisUtil.hset(key, "post:viewCount", post.getViewCount(), expireTime);//缓存浏览量
        }
    }

    //获取过期的时间
    private long getExpireTime(Post post) {
        long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);//获取可以存活多少天
        return (7 - between) * 24 * 60 * 60;
    }

}

